"""
Content Management Service
内容管理服务 - 内容查询、更新、删除、下载功能
"""

import uuid
import os
from typing import Optional, Dict, Any, List
from datetime import datetime, timedelta

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_, func, update
from sqlalchemy.orm import selectinload
import structlog

from ..models.user import User
from ..models.generated_content import GeneratedContent, ContentStatus, ContentFormat
from ..models.creation_request import CreationRequest
from ..models.folder import Folder
from ..core.database import DatabaseSession
from ..core.config import settings
from ..core.exceptions import (
    ContentNotFoundError,
    ContentValidationError,
    PermissionDeniedError,
    ValidationError,
    FileNotFoundError,
)

# 配置结构化日志
logger = structlog.get_logger(__name__)


class ContentManagementService:
    """内容管理服务类"""

    def __init__(self, db_session: DatabaseSession):
        self.db_session = db_session

    async def get_content(self, content_id: uuid.UUID, user: User) -> Optional[GeneratedContent]:
        """
        获取内容

        Args:
            content_id: 内容ID
            user: 用户对象

        Returns:
            Optional[GeneratedContent]: 内容对象，不存在返回None

        Raises:
            PermissionDeniedError: 无权访问
        """
        async with self.db_session as session:
            result = await session.execute(
                select(GeneratedContent)
                .where(GeneratedContent.id == content_id)
                .options(
                    selectinload(GeneratedContent.user),
                    selectinload(GeneratedContent.creation_request),
                    selectinload(GeneratedContent.folder),
                    selectinload(GeneratedContent.content_tags)
                )
            )
            content = result.scalar_one_or_none()

            if not content:
                return None

            # 检查权限
            if content.user_id != user.id and not user.is_admin:
                # 检查是否为公开内容
                if not content.is_public:
                    raise PermissionDeniedError("无权访问此内容")

            # 增加查看次数
            content.increment_view_count()
            await session.commit()

            return content

    async def get_user_contents(
        self,
        user: User,
        content_type: Optional[str] = None,
        status: Optional[str] = None,
        folder_id: Optional[uuid.UUID] = None,
        is_public: Optional[bool] = None,
        is_favorite: Optional[bool] = None,
        search: Optional[str] = None,
        skip: int = 0,
        limit: int = 50,
        order_by: str = "created_at_desc"
    ) -> List[GeneratedContent]:
        """
        获取用户的内容列表

        Args:
            user: 用户对象
            content_type: 内容类型过滤
            status: 状态过滤
            folder_id: 文件夹ID过滤
            is_public: 公开状态过滤
            is_favorite: 收藏状态过滤
            search: 搜索关键词
            skip: 跳过数量
            limit: 限制数量
            order_by: 排序方式

        Returns:
            List[GeneratedContent]: 内容列表
        """
        async with self.db_session as session:
            query = select(GeneratedContent).where(GeneratedContent.user_id == user.id)

            # 应用过滤条件
            if content_type:
                query = query.where(GeneratedContent.content_type == content_type)
            if status:
                query = query.where(GeneratedContent.status == status)
            if folder_id:
                query = query.where(GeneratedContent.folder_id == folder_id)
            if is_public is not None:
                query = query.where(GeneratedContent.is_public == is_public)
            if is_favorite is not None:
                query = query.where(GeneratedContent.is_favorite == is_favorite)
            if search:
                search_filter = or_(
                    GeneratedContent.title.ilike(f"%{search}%"),
                    GeneratedContent.description.ilike(f"%{search}%"),
                    GeneratedContent.prompt.ilike(f"%{search}%"),
                    GeneratedContent.tags.contains([search])
                )
                query = query.where(search_filter)

            # 排序
            order_map = {
                "created_at_desc": GeneratedContent.created_at.desc(),
                "created_at_asc": GeneratedContent.created_at.asc(),
                "updated_at_desc": GeneratedContent.updated_at.desc(),
                "title_asc": GeneratedContent.title.asc(),
                "title_desc": GeneratedContent.title.desc(),
                "view_count_desc": GeneratedContent.view_count.desc(),
                "download_count_desc": GeneratedContent.download_count.desc(),
            }
            
            if order_by in order_map:
                query = query.order_by(order_map[order_by])
            else:
                query = query.order_by(GeneratedContent.created_at.desc())

            # 分页
            query = query.offset(skip).limit(limit)

            result = await session.execute(query)
            return result.scalars().all()

    async def update_content(
        self,
        content_id: uuid.UUID,
        user: User,
        title: Optional[str] = None,
        description: Optional[str] = None,
        is_public: Optional[bool] = None,
        is_favorite: Optional[bool] = None,
        folder_id: Optional[uuid.UUID] = None,
        tags: Optional[List[str]] = None,
        **kwargs
    ) -> GeneratedContent:
        """
        更新内容

        Args:
            content_id: 内容ID
            user: 用户对象
            title: 标题
            description: 描述
            is_public: 是否公开
            is_favorite: 是否收藏
            folder_id: 文件夹ID
            tags: 标签列表
            **kwargs: 其他参数

        Returns:
            GeneratedContent: 更新后的内容对象

        Raises:
            ContentNotFoundError: 内容不存在
            PermissionDeniedError: 无权操作
            ValidationError: 参数验证失败
        """
        logger.info("更新内容", content_id=str(content_id), user_id=str(user.id))

        async with self.db_session as session:
            result = await session.execute(
                select(GeneratedContent)
                .where(GeneratedContent.id == content_id)
                .options(selectinload(GeneratedContent.folder))
            )
            content = result.scalar_one_or_none()

            if not content:
                raise ContentNotFoundError(f"内容 {content_id} 不存在")

            # 检查权限
            if content.user_id != user.id and not user.is_admin:
                raise PermissionDeniedError("无权更新此内容")

            # 验证参数
            await self._validate_update_params(title, description, folder_id, tags)

            # 更新字段
            if title is not None:
                content.title = title
            if description is not None:
                content.description = description
            if is_public is not None:
                content.is_public = is_public
            if is_favorite is not None:
                content.is_favorite = is_favorite
            if folder_id is not None:
                content.folder_id = folder_id
            if tags is not None:
                content.tags = tags

            # 更新其他字段
            for key, value in kwargs.items():
                if hasattr(content, key):
                    setattr(content, key, value)

            await session.commit()
            await session.refresh(content)

            logger.info("内容更新成功", content_id=str(content_id))
            return content

    async def delete_content(self, content_id: uuid.UUID, user: User) -> bool:
        """
        删除内容

        Args:
            content_id: 内容ID
            user: 用户对象

        Returns:
            bool: 是否成功删除

        Raises:
            ContentNotFoundError: 内容不存在
            PermissionDeniedError: 无权操作
        """
        logger.info("删除内容", content_id=str(content_id), user_id=str(user.id))

        async with self.db_session as session:
            result = await session.execute(
                select(GeneratedContent)
                .where(GeneratedContent.id == content_id)
            )
            content = result.scalar_one_or_none()

            if not content:
                raise ContentNotFoundError(f"内容 {content_id} 不存在")

            # 检查权限
            if content.user_id != user.id and not user.is_admin:
                raise PermissionDeniedError("无权删除此内容")

            # 检查是否可以删除
            if content.status == ContentStatus.PROCESSING:
                raise ValidationError("内容正在处理中，无法删除")

            # 删除实际文件（如果存在）
            if content.file_path:
                await self._delete_content_file(content.file_path)

            # 删除缩略图（如果存在）
            if content.thumbnail_path:
                await self._delete_content_file(content.thumbnail_path)

            # 删除数据库记录
            await session.delete(content)
            await session.commit()

            logger.info("内容删除成功", content_id=str(content_id))
            return True

    async def download_content(
        self,
        content_id: uuid.UUID,
        user: User
    ) -> Optional[Dict[str, Any]]:
        """
        下载内容

        Args:
            content_id: 内容ID
            user: 用户对象

        Returns:
            Optional[Dict[str, Any]]: 下载信息，包含文件路径和元数据

        Raises:
            ContentNotFoundError: 内容不存在
            PermissionDeniedError: 无权下载
            FileNotFoundError: 文件不存在
        """
        logger.info("下载内容", content_id=str(content_id), user_id=str(user.id))

        async with self.db_session as session:
            result = await session.execute(
                select(GeneratedContent)
                .where(GeneratedContent.id == content_id)
            )
            content = result.scalar_one_or_none()

            if not content:
                raise ContentNotFoundError(f"内容 {content_id} 不存在")

            # 检查权限
            if content.user_id != user.id and not content.is_public and not user.is_admin:
                raise PermissionDeniedError("无权下载此内容")

            # 检查文件是否存在
            if not content.file_path or not os.path.exists(content.file_path):
                raise FileNotFoundError(f"内容文件不存在: {content.file_path}")

            # 更新下载次数
            content.increment_download_count()
            await session.commit()

            logger.info("内容下载成功", content_id=str(content_id), file_path=content.file_path)

            return {
                "file_path": content.file_path,
                "file_name": content.file_name,
                "file_size": content.file_size_bytes,
                "content_type": content.content_type,
                "format": content.format,
                "title": content.title,
                "description": content.description
            }

    async def batch_delete_contents(
        self,
        content_ids: List[uuid.UUID],
        user: User
    ) -> Dict[str, Any]:
        """
        批量删除内容

        Args:
            content_ids: 内容ID列表
            user: 用户对象

        Returns:
            Dict[str, Any]: 删除结果统计

        Raises:
            ValidationError: 参数验证失败
        """
        logger.info("批量删除内容", content_count=len(content_ids), user_id=str(user.id))

        if not content_ids:
            raise ValidationError("内容ID列表不能为空")

        if len(content_ids) > 100:
            raise ValidationError("一次最多删除100个内容")

        async with self.db_session as session:
            # 获取要删除的内容
            result = await session.execute(
                select(GeneratedContent)
                .where(GeneratedContent.id.in_(content_ids))
            )
            contents = result.scalars().all()

            # 检查权限和状态
            deletable_contents = []
            errors = []

            for content in contents:
                content_id = str(content.id)
                
                # 检查权限
                if content.user_id != user.id and not user.is_admin:
                    errors.append({
                        "content_id": content_id,
                        "error": "无权删除此内容"
                    })
                    continue

                # 检查状态
                if content.status == ContentStatus.PROCESSING:
                    errors.append({
                        "content_id": content_id,
                        "error": "内容正在处理中，无法删除"
                    })
                    continue

                deletable_contents.append(content)

            # 删除内容
            deleted_count = 0
            for content in deletable_contents:
                try:
                    # 删除文件
                    if content.file_path:
                        await self._delete_content_file(content.file_path)
                    if content.thumbnail_path:
                        await self._delete_content_file(content.thumbnail_path)

                    # 删除数据库记录
                    await session.delete(content)
                    deleted_count += 1
                except Exception as e:
                    errors.append({
                        "content_id": str(content.id),
                        "error": f"删除失败: {str(e)}"
                    })

            await session.commit()

            logger.info("批量删除内容完成", 
                       deleted_count=deleted_count, 
                       error_count=len(errors),
                       total_count=len(content_ids))

            return {
                "deleted_count": deleted_count,
                "error_count": len(errors),
                "errors": errors,
                "total_count": len(content_ids)
            }

    async def move_content_to_folder(
        self,
        content_ids: List[uuid.UUID],
        folder_id: Optional[uuid.UUID],
        user: User
    ) -> Dict[str, Any]:
        """
        移动内容到文件夹

        Args:
            content_ids: 内容ID列表
            folder_id: 目标文件夹ID（None表示移动到根目录）
            user: 用户对象

        Returns:
            Dict[str, Any]: 移动结果统计

        Raises:
            ValidationError: 参数验证失败
            FolderNotFoundError: 文件夹不存在
        """
        logger.info("移动内容到文件夹", 
                   content_count=len(content_ids), 
                   folder_id=str(folder_id) if folder_id else None,
                   user_id=str(user.id))

        if not content_ids:
            raise ValidationError("内容ID列表不能为空")

        if len(content_ids) > 100:
            raise ValidationError("一次最多移动100个内容")

        async with self.db_session as session:
            # 验证目标文件夹
            if folder_id:
                from ..models.folder import Folder
                folder_result = await session.execute(
                    select(Folder).where(Folder.id == folder_id)
                )
                folder = folder_result.scalar_one_or_none()
                
                if not folder:
                    raise ContentNotFoundError(f"文件夹 {folder_id} 不存在")
                
                if folder.user_id != user.id and not user.is_admin:
                    raise PermissionDeniedError("无权访问目标文件夹")

            # 获取要移动的内容
            result = await session.execute(
                select(GeneratedContent)
                .where(GeneratedContent.id.in_(content_ids))
            )
            contents = result.scalars().all()

            # 检查权限
            movable_contents = []
            errors = []

            for content in contents:
                content_id = str(content.id)
                
                # 检查权限
                if content.user_id != user.id and not user.is_admin:
                    errors.append({
                        "content_id": content_id,
                        "error": "无权移动此内容"
                    })
                    continue

                movable_contents.append(content)

            # 移动内容
            moved_count = 0
            for content in movable_contents:
                content.folder_id = folder_id
                moved_count += 1

            await session.commit()

            logger.info("移动内容到文件夹完成", 
                       moved_count=moved_count, 
                       error_count=len(errors),
                       total_count=len(content_ids))

            return {
                "moved_count": moved_count,
                "error_count": len(errors),
                "errors": errors,
                "total_count": len(content_ids)
            }

    async def add_content_tags(
        self,
        content_id: uuid.UUID,
        tags: List[str],
        user: User
    ) -> GeneratedContent:
        """
        添加内容标签

        Args:
            content_id: 内容ID
            tags: 标签列表
            user: 用户对象

        Returns:
            GeneratedContent: 更新后的内容对象

        Raises:
            ContentNotFoundError: 内容不存在
            PermissionDeniedError: 无权操作
        """
        if not tags:
            raise ValidationError("标签列表不能为空")

        async with self.db_session as session:
            result = await session.execute(
                select(GeneratedContent)
                .where(GeneratedContent.id == content_id)
            )
            content = result.scalar_one_or_none()

            if not content:
                raise ContentNotFoundError(f"内容 {content_id} 不存在")

            # 检查权限
            if content.user_id != user.id and not user.is_admin:
                raise PermissionDeniedError("无权更新此内容")

            # 添加标签
            current_tags = content.tags or []
            for tag in tags:
                if tag not in current_tags:
                    content.add_tag(tag)

            await session.commit()
            await session.refresh(content)

            return content

    async def remove_content_tags(
        self,
        content_id: uuid.UUID,
        tags: List[str],
        user: User
    ) -> GeneratedContent:
        """
        移除内容标签

        Args:
            content_id: 内容ID
            tags: 要移除的标签列表
            user: 用户对象

        Returns:
            GeneratedContent: 更新后的内容对象

        Raises:
            ContentNotFoundError: 内容不存在
            PermissionDeniedError: 无权操作
        """
        if not tags:
            raise ValidationError("标签列表不能为空")

        async with self.db_session as session:
            result = await session.execute(
                select(GeneratedContent)
                .where(GeneratedContent.id == content_id)
            )
            content = result.scalar_one_or_none()

            if not content:
                raise ContentNotFoundError(f"内容 {content_id} 不存在")

            # 检查权限
            if content.user_id != user.id and not user.is_admin:
                raise PermissionDeniedError("无权更新此内容")

            # 移除标签
            for tag in tags:
                content.remove_tag(tag)

            await session.commit()
            await session.refresh(content)

            return content

    async def get_content_statistics(self, user: User) -> Dict[str, Any]:
        """
        获取用户内容统计

        Args:
            user: 用户对象

        Returns:
            Dict[str, Any]: 统计信息
        """
        async with self.db_session as session:
            # 总体统计
            total_result = await session.execute(
                select(
                    func.count(GeneratedContent.id),
                    func.count(GeneratedContent.id).filter(GeneratedContent.status == ContentStatus.COMPLETED),
                    func.count(GeneratedContent.id).filter(GeneratedContent.status == ContentStatus.FAILED),
                    func.count(GeneratedContent.id).filter(GeneratedContent.is_public == True),
                    func.count(GeneratedContent.id).filter(GeneratedContent.is_favorite == True),
                    func.sum(GeneratedContent.file_size_bytes)
                ).where(GeneratedContent.user_id == user.id)
            )
            total, completed, failed, public, favorite, total_size = total_result.first()

            # 按内容类型统计
            by_type_result = await session.execute(
                select(
                    GeneratedContent.content_type,
                    func.count(GeneratedContent.id)
                )
                .where(GeneratedContent.user_id == user.id)
                .group_by(GeneratedContent.content_type)
            )
            by_type = dict(by_type_result.all())

            # 按格式统计
            by_format_result = await session.execute(
                select(
                    GeneratedContent.format,
                    func.count(GeneratedContent.id)
                )
                .where(GeneratedContent.user_id == user.id)
                .group_by(GeneratedContent.format)
            )
            by_format = dict(by_format_result.all())

            return {
                "user_id": str(user.id),
                "total_contents": total or 0,
                "completed_contents": completed or 0,
                "failed_contents": failed or 0,
                "public_contents": public or 0,
                "favorite_contents": favorite or 0,
                "total_storage_mb": round((total_size or 0) / (1024 * 1024), 2),
                "success_rate": (completed / total * 100) if total > 0 else 0,
                "by_content_type": by_type,
                "by_format": by_format,
            }

    async def search_contents(
        self,
        user: User,
        query: str,
        content_type: Optional[str] = None,
        limit: int = 50
    ) -> List[GeneratedContent]:
        """
        搜索内容

        Args:
            user: 用户对象
            query: 搜索查询
            content_type: 内容类型过滤
            limit: 限制数量

        Returns:
            List[GeneratedContent]: 搜索结果列表

        Raises:
            ValidationError: 查询参数无效
        """
        if not query or len(query.strip()) < 2:
            raise ValidationError("搜索查询至少需要2个字符")

        if len(query) > 200:
            raise ValidationError("搜索查询过长，最大200字符")

        async with self.db_session as session:
            # 构建搜索查询
            search_query = select(GeneratedContent).where(GeneratedContent.user_id == user.id)

            # 搜索条件
            search_filter = or_(
                GeneratedContent.title.ilike(f"%{query}%"),
                GeneratedContent.description.ilike(f"%{query}%"),
                GeneratedContent.prompt.ilike(f"%{query}%"),
                GeneratedContent.tags.contains([query])
            )
            search_query = search_query.where(search_filter)

            # 内容类型过滤
            if content_type:
                search_query = search_query.where(GeneratedContent.content_type == content_type)

            # 排序（按相关性简单排序）
            search_query = search_query.order_by(
                # 标题匹配的优先级最高
                func.case(
                    (GeneratedContent.title.ilike(f"%{query}%"), 1),
                    else_=2
                ).asc(),
                # 然后按创建时间排序
                GeneratedContent.created_at.desc()
            ).limit(limit)

            result = await session.execute(search_query)
            return result.scalars().all()

    async def get_recent_contents(
        self,
        user: User,
        days: int = 7,
        limit: int = 20
    ) -> List[GeneratedContent]:
        """
        获取最近的内容

        Args:
            user: 用户对象
            days: 最近天数
            limit: 限制数量

        Returns:
            List[GeneratedContent]: 最近的内容列表
        """
        cutoff_date = datetime.utcnow() - timedelta(days=days)

        async with self.db_session as session:
            result = await session.execute(
                select(GeneratedContent)
                .where(
                    and_(
                        GeneratedContent.user_id == user.id,
                        GeneratedContent.created_at >= cutoff_date
                    )
                )
                .order_by(GeneratedContent.created_at.desc())
                .limit(limit)
            )
            return result.scalars().all()

    async def cleanup_expired_contents(self, max_age_days: int = 90) -> int:
        """
        清理过期的内容（系统维护任务）

        Args:
            max_age_days: 最大保留天数

        Returns:
            int: 清理的内容数量
        """
        cutoff_date = datetime.utcnow() - timedelta(days=max_age_days)

        async with self.db_session as session:
            result = await session.execute(
                select(GeneratedContent)
                .where(
                    and_(
                        GeneratedContent.created_at < cutoff_date,
                        GeneratedContent.is_favorite == False,  # 不删除收藏的内容
                        GeneratedContent.download_count == 0    # 不删除已下载的内容
                    )
                )
            )
            expired_contents = result.scalars().all()

            # 删除过期内容
            deleted_count = 0
            for content in expired_contents:
                try:
                    # 删除文件
                    if content.file_path:
                        await self._delete_content_file(content.file_path)
                    if content.thumbnail_path:
                        await self._delete_content_file(content.thumbnail_path)

                    # 删除数据库记录
                    await session.delete(content)
                    deleted_count += 1
                except Exception as e:
                    logger.error(f"清理内容失败: {content.id}", error=str(e))

            await session.commit()

            logger.info("清理过期内容完成", cleaned_count=deleted_count, max_age_days=max_age_days)
            return deleted_count

    # 私有辅助方法

    async def _validate_update_params(
        self,
        title: Optional[str],
        description: Optional[str],
        folder_id: Optional[uuid.UUID],
        tags: Optional[List[str]]
    ) -> None:
        """验证更新参数"""
        if title is not None and len(title) > 200:
            raise ValidationError("标题过长，最大200字符")
        
        if description is not None and len(description) > 2000:
            raise ValidationError("描述过长，最大2000字符")
        
        if tags is not None:
            if len(tags) > 50:
                raise ValidationError("标签数量过多，最多50个")
            for tag in tags:
                if len(tag) > 50:
                    raise ValidationError(f"标签 '{tag}' 过长，最大50字符")

    async def _delete_content_file(self, file_path: str) -> None:
        """删除内容文件"""
        try:
            if os.path.exists(file_path):
                os.remove(file_path)
                logger.debug("删除文件成功", file_path=file_path)
        except Exception as e:
            logger.warning("删除文件失败", file_path=file_path, error=str(e))

    async def _validate_content_access(
        self,
        content: GeneratedContent,
        user: User,
        require_write: bool = False
    ) -> None:
        """验证内容访问权限"""
        # 检查用户权限
        if content.user_id != user.id and not user.is_admin:
            # 检查是否为公开内容
            if not content.is_public:
                raise PermissionDeniedError("无权访问此内容")
            
            # 如果需要写权限，公开内容也不行
            if require_write:
                raise PermissionDeniedError("无权修改此内容")

        # 检查内容状态
        if require_write and content.status == ContentStatus.PROCESSING:
            raise ValidationError("内容正在处理中，无法修改")